#include "TinyMsg.h"

#include<string.h>

namespace TinyCC{
	namespace msg{
		CTinyMsg::CTinyMsg(void):m_pMsgData(NULL)
		{
			m_sourceIP[0]=0;
			m_sourceIP[1]=0;
			m_sourceIP[2]=0;
			m_sourceIP[3]=0;
			m_socketID =0;
		}

		//copy ctor
		CTinyMsg::CTinyMsg(const CTinyMsg & tinymsg):m_pMsgData(NULL)
		{
			m_sourceIP[0]=0;
			m_sourceIP[1]=0;
			m_sourceIP[2]=0;
			m_sourceIP[3]=0;
			m_socketID =0;

			long len=0;
			const char* p=NULL;
			CTinyMsg* pMsg=(CTinyMsg*)&tinymsg;
			p=pMsg->serialize(len);
			if(p!=NULL)
				deSerialize(p,len);
		}

		CTinyMsg::~CTinyMsg()
		{
			clear();
			if(m_pMsgData!=NULL&&m_pMsgData!=(char*)&m_Fixedbuffer){
				delete[] m_pMsgData;
				m_pMsgData=NULL;
			}
		}
		//clear internel data
		void CTinyMsg::clear()
		{
			//clear msghead
			m_msgHead.m_magicID=0x3333;
			m_msgHead.m_msgIndex=0;
			m_msgHead.m_msgItem=0;
			m_msgHead.m_checkSum=0;
			m_msgHead.m_defaultParamCount=0;
			m_msgHead.m_ExternalParamMapCount=0;
			m_msgHead.m_sourcePort=0;
			memset(&m_msgHead.m_sourceIP,0,4*sizeof(BYTE));


			//clear default param
			CTinyMetaElement* pMValue=NULL;
			std::list<CTinyMetaElement*>::iterator itEnumDP=m_defaultParams.begin();
			while(itEnumDP!=m_defaultParams.end())
			{
				pMValue=*itEnumDP;
				if(pMValue!=NULL)
					delete pMValue;
				itEnumDP++;
			}//while
			m_defaultParams.clear();

			//clear External param

			CTinyParamMap* pEPMap=NULL;
			std::map<int,CTinyParamMap*>::iterator itEnumEPItor=m_ExternalParamsMap.begin();
			while(itEnumEPItor!=m_ExternalParamsMap.end())
			{
				pEPMap=itEnumEPItor->second;
				if(pEPMap!=NULL)
					delete pEPMap;
				itEnumEPItor++;
			}//while
			m_ExternalParamsMap.clear();
		}


		const char* CTinyMsg::getSourceIP()
		{
			sprintf_s( m_sourceIP, 32, "%d.%d.%d.%d",
				m_msgHead.m_sourceIP[0],m_msgHead.m_sourceIP[1],
				m_msgHead.m_sourceIP[2],m_msgHead.m_sourceIP[3]);
			return m_sourceIP;
		}

		void CTinyMsg::setSourceIP(const char *ip)
		{
			if(ip == NULL)
				return;

			const char* pszAddr = ip;
			long Flag,Len;
			char OneIP[8];
			BYTE IPAddr;

			for(int i =0;i<4;++i)
			{
				Flag = strcspn(pszAddr,".");
				Len = strlen(pszAddr);
				if(Flag <= 0 || Flag > Len)
					return;
				memset(OneIP,0,8);
				memcpy(OneIP,pszAddr,Flag);
				IPAddr = atoi(OneIP);
				pszAddr += Flag + 1;
				m_msgHead.m_sourceIP[i] = IPAddr;
			}

		}

		WORD CTinyMsg::getSourcePort()
		{
			return m_msgHead.m_sourcePort;
		}


		long CTinyMsg::pushBackDefaultParam(const char *value)
		{
			if(value == NULL)
				return 0;
			std::string valuestr=value;
			return pushBackDefaultParam(valuestr);
		}

		long CTinyMsg::pushBackDefaultParam(std::string& value)
		{
			CTinyMetaElement *pValue=new CTinyMetaElement(value.c_str(),value.length());
			return pushBackDefaultParam(pValue);
		}

		long CTinyMsg::pushBackDefaultParam(CTinyMetaElement* value)
		{
			if(value == NULL )
				return 0;

			m_defaultParams.push_back(value);

			m_msgHead.m_defaultParamCount++;

			return m_msgHead.m_defaultParamCount;
		}

		bool CTinyMsg::setDefaultParam(int index,CTinyMetaElement* value)
		{
			if(value == NULL )
				return 0;
			if(index>m_msgHead.m_defaultParamCount)
				return false;

			if(index == m_msgHead.m_defaultParamCount)
			{
				this->pushBackDefaultParam(value);
				return true;
			}

			std::list<CTinyMetaElement*>::iterator  itFindDP=m_defaultParams.begin();
			while(itFindDP!=m_defaultParams.end())
			{
				if(index==0){
					delete *itFindDP;
					*itFindDP = value;
					return true;
				}//if
				index--;
				itFindDP++;
			}//while
			return false;
		}


		int CTinyMsg::getDefaultParamCount()
		{
			return m_msgHead.m_defaultParamCount;
		}

		CTinyMetaElement* CTinyMsg::getDefaultParamByIndex(int defaultParamIndex)
		{
			if(defaultParamIndex < 0 || defaultParamIndex > m_msgHead.m_defaultParamCount)
			{
				return false;
			}

			std::list<CTinyMetaElement*>::iterator  itFindDP=m_defaultParams.begin();
			while(itFindDP!=m_defaultParams.end())
			{
				if(defaultParamIndex==0){
					return *itFindDP;
				}//if
				defaultParamIndex--;
				itFindDP++;
			}//while

			return NULL;
		}

		bool CTinyMsg::getDefaultParamByIndex(int defaultParamIndex, std::string& value)
		{
			long len = 0;
			CTinyMetaElement* pValue=getDefaultParamByIndex( defaultParamIndex );

			if(pValue==NULL)
				return false;

			const char* p=pValue->getData();
			len=pValue->getDataLength();

			if( len == 0 )
				value="";
			else if( p != NULL )
				value.assign( p, len );
			else
				return false;
			return true;
		}
		long CTinyMsg::pushExternalParamByKey(int epindex, const char *key, const char *value)
		{
			if(key == NULL || value == NULL)
				return 0;
			std::string keystr=key;
			std::string valuestr=value;
			return pushExternalParamByKey(epindex,keystr,valuestr);
		}

		long CTinyMsg::pushExternalParamByKey(int epindex,std::string& key,std::string& value)
		{
			CTinyMetaElement *pKey=new CTinyMetaElement(key.c_str(),key.length());
			CTinyMetaElement *pValue=new CTinyMetaElement(value.c_str(),value.length());
			return pushExternalParamByKey(epindex,pKey,pValue);
		}
		long CTinyMsg::pushExternalParamByKey(int epindex, CTinyMetaElement* key, CTinyMetaElement* value)
		{
			if(epindex > m_msgHead.m_defaultParamCount || epindex < 0)
				return 0;

			if(key == NULL  || value ==NULL )
				return 0;
			CTinyParamMap* pNPCluster=NULL;
			std::map<int,CTinyParamMap*>::iterator itFindNpcr=m_ExternalParamsMap.find(epindex);
			if(itFindNpcr==m_ExternalParamsMap.end())//not found
			{
				pNPCluster=new CTinyParamMap(epindex);
				if(pNPCluster==NULL)
					return -1;
				m_ExternalParamsMap.insert( std::pair< int, CTinyParamMap* >( epindex, pNPCluster ) );
				m_msgHead.m_ExternalParamMapCount++;
			}
			else
				pNPCluster=itFindNpcr->second;

			return pNPCluster->pushParam(key,value);

		}

		int CTinyMsg::getExternalParamKeyCountByIndex(int epindex)
		{
			if(epindex >m_msgHead.m_ExternalParamMapCount || epindex < 0)
				return 0;

			std::map<int,CTinyParamMap*>::iterator itFindNPSpace=m_ExternalParamsMap.find(epindex);
			if(itFindNPSpace!=m_ExternalParamsMap.end())
			{
				CTinyParamMap* pNPCluster=itFindNPSpace->second;
				if(pNPCluster!=NULL)
				{
					return pNPCluster->getParamCount();
				}
			}//if


			return 0;
		}


		int CTinyMsg::getExternalParamValueCountByKey(int epindex, const char *key)
		{
			//validate paramter
			if(epindex >m_msgHead.m_ExternalParamMapCount || epindex < 0)
				return NULL;
			if(key == NULL)
				return 0;


			std::map<int,CTinyParamMap*>::iterator itFindNPSpace=m_ExternalParamsMap.find(epindex);
			if(itFindNPSpace!=m_ExternalParamsMap.end())
			{
				CTinyParamMap* pNPCluster=itFindNPSpace->second;
				if(pNPCluster!=NULL)
				{
					return pNPCluster->getParamValueCount(key);
				}
			}//if

			return 0;
		}


		//get value
		CTinyMetaElement* CTinyMsg::getExternalParamValueByEmunator(int epindex, CTinyMetaElement* key, 
			int externalParamValueIndex)
		{
			if(epindex > m_msgHead.m_ExternalParamMapCount|| epindex < 0)
				return false;
			if(key == NULL || externalParamValueIndex < 0)
				return false;

			std::map<int,CTinyParamMap*>::iterator itFindEPMap=m_ExternalParamsMap.find(epindex);
			if(itFindEPMap!=m_ExternalParamsMap.end())
			{
				CTinyParamMap* itFind=itFindEPMap->second;
				if(itFind!=NULL)
				{
					return itFind->getParamValue(key,externalParamValueIndex);
				}
			}//if

			return NULL;

		}

		CTinyMetaElement* CTinyMsg::getExternalParamValueByEmunator(int epindex,std::string& key,int externalParamValueIndex)
		{
			CTinyMetaElement* pKey=new CTinyMetaElement(key.c_str(),key.size());
			CTinyMetaElement* value = getExternalParamValueByEmunator( epindex, pKey, externalParamValueIndex);
			delete pKey;
			return value;

		}
		CTinyMetaElement* CTinyMsg::getExternalParamValueByEmunator(int epindex,const char* key,int externalParamValueIndex)
		{
			std::string keystr=key;
			CTinyMetaElement* pKey=new CTinyMetaElement(keystr.c_str(),keystr.size());
			CTinyMetaElement* value = getExternalParamValueByEmunator( epindex, pKey, externalParamValueIndex);
			delete pKey;
			return value;
		}

		bool CTinyMsg::getExternalParamValueByEmunator(int epindex,std::string& key,int externalParamValueIndex,std::string& value)
		{
			CTinyMetaElement* pKey=new CTinyMetaElement(key.c_str(),key.size());
			CTinyMetaElement* pValue=getExternalParamValueByEmunator( epindex, pKey, externalParamValueIndex);
			delete pKey;

			if( pValue == NULL )
				return false;

			if( pValue->getDataLength() == -1 )
				return false;
			else if( pValue->getDataLength()==0 )
				value="";
			else if( pValue->getData() != NULL )
				value.assign( pValue->getData(), pValue->getDataLength() );
			else
				return false;
			return true;
		}
		bool CTinyMsg::getExternalParamValueByEmunator(int epindex,const char* key,int externalParamValueIndex, std::string& value)
		{
			CTinyMetaElement* pValue=getExternalParamValueByEmunator( epindex, key, externalParamValueIndex);

			if( pValue == NULL )
				return false;

			if( pValue->getDataLength() == -1 )
				return false;
			else if( pValue->getDataLength()==0 )
				value="";
			else if( pValue->getData() != NULL )
				value.assign( pValue->getData(), pValue->getDataLength() );
			else
				return false;
			return true;
		}

		int CTinyMsg::_EnumnerateEPValueCountByKey(int epindex,int keyIndex)
		{
			//validate parameter
			if(epindex > m_msgHead.m_ExternalParamMapCount || epindex < 0)
				return 0;
			if(keyIndex <= 0)
				return 0;

			std::map<int,CTinyParamMap*>::iterator itFindNPSpace=m_ExternalParamsMap.find(epindex);
			if(itFindNPSpace!=m_ExternalParamsMap.end())
			{
				CTinyParamMap* pNPCluster=itFindNPSpace->second;
				if(pNPCluster!=NULL)
				{
					return pNPCluster->getParamValueCount(keyIndex);
				}
			}//if

			return 0;
		}

		CTinyMetaElement* CTinyMsg::_EnumnerateEPValueByIndex(int epindex,int keyIndex,int externalParamValueIndex)
		{

			if(epindex > m_msgHead.m_ExternalParamMapCount || epindex < 0)
				return NULL;
			if(keyIndex <=0 || externalParamValueIndex <= 0)
				return NULL;


			//Get key from specified index by reference,namedParamIndex is base 1,namedParamValueIndex is base 1
			std::map<int,CTinyParamMap*>::iterator itFindNPSpace=m_ExternalParamsMap.find(epindex);
			if(itFindNPSpace!=m_ExternalParamsMap.end())
			{
				CTinyParamMap* pNPCluster=itFindNPSpace->second;
				if(pNPCluster!=NULL)
				{
					return pNPCluster->getParamValue(keyIndex,externalParamValueIndex);
				}
			}//if
			return NULL;
		}

		CTinyMetaElement* CTinyMsg::_EnumnerateEPKeyByIndex(int epindex,int keyIndex)
		{
			if(epindex >m_msgHead.m_ExternalParamMapCount || epindex < 0)
				return NULL;
			if(keyIndex < 0)
				return NULL;


			std::map<int,CTinyParamMap*>::iterator itFindNPSpace=m_ExternalParamsMap.find(epindex);
			if(itFindNPSpace!=m_ExternalParamsMap.end())
			{
				CTinyParamMap* pNPCluster=itFindNPSpace->second;
				if(pNPCluster!=NULL)
				{
					return pNPCluster->getParamKey(keyIndex);
				}
			}//if
			return NULL;
		}

		bool CTinyMsg::_EnumnerateEPKeyByIndex(int epindex,int keyIndex,std::string& key)
		{
			CTinyMetaElement* pKey=_EnumnerateEPKeyByIndex(epindex,keyIndex);
			if( pKey == NULL )
				return false;

			if( pKey->getDataLength() == -1 )
				return false;
			else if( pKey->getDataLength()==0 )
				key="";
			else if( pKey->getData() != NULL )
				key.assign( pKey->getData(), pKey->getDataLength() );
			else
				return false;
			return true;


		}

		CTinyMetaElement* CTinyMsg::_EnumnerateEPValueByEnumnator(int epindex,int keyIndex,int externalParamValueIndex)
		{
			if(epindex > m_msgHead.m_ExternalParamMapCount || epindex < 0)
				return NULL;
			if(keyIndex <=0 || externalParamValueIndex < 0)
				return NULL;


			//Get key from specified index by reference,namedParamIndex is base 1,namedParamValueIndex is base 1
			std::map<int,CTinyParamMap*>::iterator itFindNPSpace=m_ExternalParamsMap.find(epindex);
			if(itFindNPSpace!=m_ExternalParamsMap.end())
			{
				CTinyParamMap* pNPCluster=itFindNPSpace->second;
				if(pNPCluster!=NULL)
				{
					return pNPCluster->getParamValue(keyIndex,externalParamValueIndex);
				}
			}//if


			return NULL;
		}
		bool CTinyMsg::_EnumnerateEPValueByEnumnator(int epindex,int keyIndex,int externalParamValueIndex, std::string& value)
		{
			CTinyMetaElement* pValue=_EnumnerateEPValueByEnumnator( epindex, keyIndex, externalParamValueIndex );

			if(pValue=NULL)
				return false;

			if( pValue->getDataLength() == -1 )
				return false;
			else if(  pValue->getDataLength() == 0 )
				value="";
			else if(  pValue->getData()!= NULL )
				value.assign(  pValue->getData(),  pValue->getDataLength() );
			else
				return false;
			return true;
		}
		//end reserve

		const char* CTinyMsg::serialize(long& length)
		{
			//1.get serialize size
			long serializeSize=4/*Reserve 4 bytes for binary message*/+sizeof(CTinyMsgHeader);

			CTinyMetaElement* pMValue=NULL;
			std::list<CTinyMetaElement*>::iterator itEnumDP=m_defaultParams.begin();
			while(itEnumDP!=m_defaultParams.end())
			{
				pMValue=*itEnumDP;
				if(pMValue!=NULL)
					serializeSize+=pMValue->getSerializeSize();//sizeof(DWORD)/*A DWORD is required to save the length of metadata*/+pMValue->getDataLength();
				itEnumDP++;
			}//while

			CTinyParamMap* pNPCluster=NULL;
			std::map<int,CTinyParamMap*>::iterator itEnumNPCluster=this->m_ExternalParamsMap.begin();
			while(itEnumNPCluster!=m_ExternalParamsMap.end())
			{
				pNPCluster=itEnumNPCluster->second;
				if(pNPCluster!=NULL)
				{
					serializeSize+=pNPCluster->getSerializeSize();
				}//if
				itEnumNPCluster++;
			}//while
			//end get serialize size

			//2.Alloc memory
			if(serializeSize<=FIXED_MSG_LENGTH)
			{
				m_pMsgData=(char*)&m_Fixedbuffer;
				memset(m_pMsgData,0,FIXED_MSG_LENGTH);
			}
			else
			{
				if(m_pMsgData==(char*)&m_Fixedbuffer)
				{
					m_pMsgData=new char[serializeSize];
				}
				else
				{
					if(m_pMsgData != NULL)
						delete[] m_pMsgData;
					m_pMsgData=new char[serializeSize];
				}
				memset(m_pMsgData,0,serializeSize);
			}
			if(m_pMsgData==NULL)
			{
				//alloc failed
				length=0;
				return NULL;
			}
			length=serializeSize;

			//3.generate check sum
			_writeCheckSum();

			//4.Copy the data into data buffer
			long lMsgLength=0;
			char* pOperatingData=m_pMsgData;
			//Copy message head
			long headerSize=sizeof(CTinyMsgHeader);
			memcpy(pOperatingData,&m_msgHead,headerSize);
			pOperatingData=pOperatingData+headerSize;
			lMsgLength+=headerSize;

			//Copy default parameter
			long dpValueLength=0;
			CTinyMetaElement* pMetaValue=NULL;
			std::list<CTinyMetaElement*>::iterator itEnumDPValue=m_defaultParams.begin();
			while(itEnumDPValue!=m_defaultParams.end())
			{
				pMetaValue=*itEnumDPValue;
				if(pMetaValue!=NULL)
				{
					pMetaValue->serialize(pOperatingData,dpValueLength);
					pOperatingData=pOperatingData+dpValueLength;
					lMsgLength+=dpValueLength;
					dpValueLength=0;
				}
				itEnumDPValue++;
			}//while

			//Copy named parameter
			long npClusterLength=0;
			CTinyParamMap* pNamedParamCluster=NULL;
			std::map<int,CTinyParamMap*>::iterator itEnumNamedParamCluster=m_ExternalParamsMap.begin();
			while(itEnumNamedParamCluster!=m_ExternalParamsMap.end())
			{
				pNamedParamCluster=itEnumNamedParamCluster->second;
				if(pNamedParamCluster!=NULL)
				{
					npClusterLength=serializeSize-lMsgLength;
					pNamedParamCluster->serialize(pOperatingData,npClusterLength);
					pOperatingData=pOperatingData+npClusterLength;
					lMsgLength+=npClusterLength;
					npClusterLength=0;
				}
				itEnumNamedParamCluster++;
			}//while
			return m_pMsgData;
		}

		bool CTinyMsg::deSerialize(const char* pMsg,long length)
		{
			if(length <= 0||pMsg == NULL)
				return false;

			DWORD *pDWord=NULL;
			WORD *pWord=NULL;
			size_t headerSize=sizeof(m_msgHead);
			if(length<headerSize)
				return false;

			clear();

			//message head
			char* p=(char*)pMsg;
			memcpy(&m_msgHead,pMsg,headerSize);
			p=p+headerSize;

			//Message head verify
			if(!_verifyCheckSum())
				return false;

			//default param
			for(int i=1;i<=m_msgHead.m_defaultParamCount;i++)
			{
				//the head of default parameter metadata takes 2 bytes,stores the length of the value
				pDWord=(DWORD*)p;
				p=p+sizeof(DWORD);
				CTinyMetaElement* pMValue=new CTinyMetaElement(p,*pDWord);
				if(pMValue==NULL)
					return FALSE;
				m_defaultParams.push_back(pMValue);
				p=p+*pDWord;
			}//for i


			//external param
			WORD epindex=0;
			for(int i=1;i<=m_msgHead.m_ExternalParamMapCount;i++)
			{
				//Index of Named parameter cluster task 1 byte.
				pWord=(WORD*)p;
				p=p+sizeof(WORD);
				epindex=*pWord;
				CTinyParamMap* pNPCluster=new CTinyParamMap(epindex);
				if(pNPCluster==NULL)
					return false;
				long NPLength=0;
				if(!pNPCluster->deSerialize(p,NPLength))
				{
					delete pNPCluster;
					return false;
				}
				p+=NPLength;
				m_ExternalParamsMap.insert( std::pair< int, CTinyParamMap* >( epindex, pNPCluster ) );
			}//for i

			return true;
		}

		void CTinyMsg::_writeCheckSum()
		{

			BYTE* pb = (BYTE*)(&m_msgHead);
			m_msgHead.m_checkSum = 0;
			for(int i = 0; i < sizeof(CTinyMsgHeader) - sizeof(WORD); i++){
				m_msgHead.m_checkSum += *pb;
				pb++; 
			}

		}

		BOOL CTinyMsg::_verifyCheckSum()
		{
			BYTE* pb = (BYTE*)(&m_msgHead);
			WORD checkSum = 0;
			for(int i = 0; i < sizeof(CTinyMsgHeader) - sizeof(WORD); i++){
				checkSum += *pb;
				pb++; 
			}
			if(checkSum == m_msgHead.m_checkSum)
				return TRUE;
			else
				return FALSE;
			return TRUE;
		}

		bool CTinyMsg::setDefaultParam(int index,char* value)
		{
			if(value == NULL)
				return 0;
			std::string valuestr=value;
			return setDefaultParam(index,valuestr);
		}

		bool CTinyMsg::setDefaultParam(int index,std::string& value)
		{
			if(index>getDefaultParamCount())
				return false;
			else
			{
				CTinyMetaElement *pValue=new CTinyMetaElement(value.c_str(),value.length());
				return setDefaultParam(index,pValue);
			}
		}
	}//end namespace

}
